package ddz.core;

import com.kaka.util.ObjectPool;
import ddz.core.dealer.Dealer;
import ddz.db.entity.BaseTable;
import ddz.db.entity.TableInfo;
import ddz.utils.JsonUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 逻辑牌桌
 */
public class Table extends BaseTable implements Serializable, ObjectPool.Poolable {

    /**
     * 获取下一方位索引
     *
     * @param currIdx 当前方位
     * @param length  方位总数，固定为四方
     * @return 下一方位
     */
    static final public int nextSeatIndex(int currIdx, int length) {
        currIdx++;
        if (currIdx >= length) {
            currIdx = 0;
        }
        return currIdx;
    }

    /**
     * 获取上一方位索引
     *
     * @param currIdx 当前方位
     * @param length  方位总数，固定为四方
     * @return 上一方位
     */
    static final public int prevSeatIndex(int currIdx, int length) {
        currIdx--;
        if (currIdx < 0) {
            currIdx = length - 1;
        }
        return currIdx;
    }

    protected Player[] players;   //房间内的成员
    protected List<OptRecord> records;  //操作记录
    private Dealer dealer;
    private TableState state;
    private volatile int dizhuSeatIndex = -100;
    private int allPassDizhuCount = 0; //全部不要地主的次数

    public Table(Long id, int maxPlayers) {
        this.id = id;
        this.maxPlayers = maxPlayers;
        this.players = new Player[maxPlayers];
        this.records = new ArrayList<>();
    }

    public Table() {
    }

    public int getAllPassDizhuCount() {
        return allPassDizhuCount;
    }

    public void setAllPassDizhuCount(int allPassDizhuCount) {
        this.allPassDizhuCount = allPassDizhuCount;
    }

    public void setDealer(Dealer dealer) {
        this.dealer = dealer;
    }

    public void clearRecords() {
        this.records.clear();
    }

    public Player addPlayer(Player player) {
        this.players[player.getSeatIndex()] = player;
        return player;
    }

    public void addOptRecord(OptRecord record) {
        this.records.add(record);
    }

    public Player getPlayerByUid(long uid) {
        for (Player m : players) {
            if (m != null && m.getUid() == uid) {
                return m;
            }
        }
        return null;
    }

    public Player[] getPlayers() {
        return this.players;
    }

    /**
     * 发牌
     */
    public void distribute() {
        Cards[] handCardsArr = new Cards[this.players.length];
        for (int i = 0; i < handCardsArr.length; i++) {
            handCardsArr[i] = this.players[i].getMycards();
            handCardsArr[i].clear();
        }
        dealer.distribute(handCardsArr, 3);
    }

    /**
     * 获取桌面底牌
     *
     * @return 桌面底牌，即地主最后获得的牌
     */
    public int[] getDiCards() {
        if (dealer == null) return null;
        return dealer.diPai();
    }

    /**
     * 获取地主的位置索引
     *
     * @return -1抢地主过程未结束，-2全部不要地主，[0, 2]地主座位索引
     */
    public int getDizhuSeatIndex() {
        if (this.dizhuSeatIndex >= 0) return this.dizhuSeatIndex;
        int v = this.getDizhuSeatIndex(this.records, this.maxPlayers);
        this.dizhuSeatIndex = v;
        return v;
    }

    /**
     * 获取下一个等待操作的玩家座位索引
     *
     * @return 等待操作的玩家座位索引
     */
    public int getWaitOperateSeatIndex() {
        if (this.records.isEmpty()) return this.bankerSeatIndex;
        int dizhuSeat = -1000;
        int last = this.records.size() - 1;
        OptRecord lastRecord = this.records.get(last);
        if (lastRecord.getOptType() == OptType.jiaodizhu
                || lastRecord.getOptType() == OptType.bujiaodizhu
                || lastRecord.getOptType() == OptType.qiangdizhu
                || lastRecord.getOptType() == OptType.buqiangdizhu
                || lastRecord.getOptType() == OptType.jiabei1
                || lastRecord.getOptType() == OptType.jiabei2
                || lastRecord.getOptType() == OptType.jiabei3
                || lastRecord.getOptType() == OptType.buJiabei) {
            //如果最后一个为抢地主相关操作且地主已确定，那么等待操作的座位索引即为地主
            dizhuSeat = this.getDizhuSeatIndex();
            if (dizhuSeat >= 0) {
                return dizhuSeat;
            }
        }
        int lastOperatedSeatIndex = lastRecord.getSeatIndex();
        return nextSeatIndex(lastOperatedSeatIndex, this.maxPlayers);
    }

    /**
     * 获取下一轮次开始的玩家座位索引
     *
     * @return 下一轮次开始的玩家座位索引
     */
    public int getNextRoundStartSeatIndex() {
        final int size = this.records.size();
        final int baseCount = this.maxPlayers - 1;
        final int start = size - 1;
        if (start < 0) return this.bankerSeatIndex;
        int end = size - baseCount;
        if (end < 0) end = 0;
        int passCount = 0;
        for (int i = start; i >= end; i--) {
            OptRecord lastRecord = this.records.get(i);
            if (lastRecord.getOptType() == OptType.pass) {
                passCount++;
            } else if (lastRecord.getOptType() == OptType.chu) {
                passCount = 0;
            }
        }
        OptRecord lastRecord = this.records.get(start);
        if (passCount == baseCount) {
            return nextSeatIndex(lastRecord.getSeatIndex(), this.maxPlayers);
        }
        if (lastRecord instanceof ChuRecord && lastRecord.getOptType() == OptType.chu) {
            return -1;
        }
        int dizhuSeatIdx = this.getDizhuSeatIndex();
        if (dizhuSeatIdx >= 0) {
            for (int i = 0; i < size; i++) {
                OptRecord record = this.records.get(i);
                if (record.getOptType() == OptType.chu) {
                    return -1;
                }
            }
            //未出牌之前的下一轮出牌玩家都是地主
            return dizhuSeatIdx;
        }
        return -1;
    }

    /**
     * 获取最后一次出牌记录
     *
     * @return 最后一次出牌记录
     */
    public ChuRecord getLastChuRecord() {
        int size = this.records.size();
        int last = size - 1;
        for (int i = last; i >= 0; i--) {
            OptRecord record = this.records.get(i);
            if (record instanceof ChuRecord && record.getOptType() == OptType.chu) {
                return (ChuRecord) record;
            }
        }
        return null;
    }

    /**
     * 根据座位索引获取玩家对象
     *
     * @param seatIndex 玩家座位索引
     * @return 玩家对象
     */
    public Player getPlayerBySeatIndex(int seatIndex) {
        if (seatIndex < 0) return null;
        return this.players[seatIndex];
    }

    /**
     * 获取地主的位置索引
     *
     * @return -1抢地主过程未结束，-2全部不要地主，[0, 2]地主座位索引
     */
    private int getDizhuSeatIndex(List<? extends OptRecord> optRecords, int maxPlayers) {
        int size = optRecords.size();
        if (size == 0) return -1;
        OptRecord firstYaoDizhuRecord = null; //第一个要地主的索引
        OptRecord lastYaoDizhuRecord = null; //最后一个要地主的
        int buyaoDizhuCount = 0; //不要地主的数量
        int n = 0; //叫地主轮次，因为三个人，所以每三次为一轮
        for (int i = 0; i < size; i++) {
            OptRecord record = records.get(i);
            OptType optType = record.getOptType();
            if (optType == OptType.mingpai) continue;
            if (optType == OptType.chu || optType == OptType.pass) break;
            if (optType == OptType.bujiaodizhu || optType == OptType.buqiangdizhu) {
                //确定所有不要地主的次数
                buyaoDizhuCount++;
            } else if (optType == OptType.jiaodizhu || optType == OptType.qiangdizhu) {
                if (firstYaoDizhuRecord == null) {
                    //记录第一次叫地主的索引
                    firstYaoDizhuRecord = record;
                } else {
                    //记录最后一次叫地主的索引
                    lastYaoDizhuRecord = record;
                }
            }
            n++;
            if (n == maxPlayers) {
                //达到一轮后索引重置为0
                if (buyaoDizhuCount == 2) {
                    //一轮中两个不要地主则那个要地主的玩家即为地主
                    return firstYaoDizhuRecord.getSeatIndex();
                } else if (buyaoDizhuCount == maxPlayers) {
                    //全部不要地主
                    return -2;
                }
            } else {
                if (n > maxPlayers) {
                    //第二轮
                    if (firstYaoDizhuRecord.getSeatIndex() == record.getSeatIndex()) {
                        //首次要地主的位置索引与当前位置索引一致时
                        if (optType == OptType.jiaodizhu || optType == OptType.qiangdizhu) {
                            //首次要地主的玩家最后一次抢夺地主且最终定为地主
                            return firstYaoDizhuRecord.getSeatIndex();
                        } else if (optType == OptType.bujiaodizhu || optType == OptType.buqiangdizhu) {
                            //首次要地主的玩家放弃最后一次要地主的机会，则地主既定为最后一个抢地主的玩家
                            return lastYaoDizhuRecord.getSeatIndex();
                        }
                    }
                }
            }
        }
        //抢夺地主过程未结束
        return -1;
    }

    /**
     * 底牌中有一对倍数x2，底牌中有3张相同，倍数x3，底牌中有王倍数x2
     */
    public DiPaiType diPaiType(int[] diPai) {
        if (diPai.length != 3) return DiPaiType.NONE;
        int wangCount = 0; //王牌的张数
        for (int card : diPai) {
            int point = Cards.getPoint(card);
            if (point == 0) wangCount++;
        }
        if (wangCount == 1) return DiPaiType.HAS_JOKER;
        if (wangCount == 2) return DiPaiType.TWO_JOKER;
        int point0 = Cards.getPoint(diPai[0]);
        int point1 = Cards.getPoint(diPai[1]);
        int point2 = Cards.getPoint(diPai[2]);
        int suit0 = Cards.getSuit(diPai[0]);
        int suit1 = Cards.getSuit(diPai[1]);
        int suit2 = Cards.getSuit(diPai[2]);
        if (point0 == point1 && point1 == point2) {
            //三张相同
            return DiPaiType.HAS_SAN;
        }
        if (point0 == point1 || point1 == point2) {
            //两张相同
            return DiPaiType.HAS_DUI;
        }
        if (Math.abs(point0 - point1) == 1 && Math.abs(point2 - point1) == 1) {
            //顺子
            if (suit0 == suit1 && suit1 == suit2) {
                //同花顺
                return DiPaiType.SAME_SUIT_SHUN;
            }
            return DiPaiType.IS_SHUN;
        }
        if (suit0 == suit1 && suit1 == suit2) {
            //同花
            return DiPaiType.SAME_SUIT;
        }
        return DiPaiType.NONE;
    }

    /**
     * 获取倍率
     *
     * @return 房间内每个玩家的倍率
     */
    public int[] getMultiple() {
        if (this.getMode() == GameMode.LIAN_WIN.getId()) {
            return new int[this.maxPlayers];
        }
        int multiple = this.baseMultiple;
        int max = records.size();
        int[] beis = new int[this.maxPlayers];
        int[] mingBeis = new int[this.maxPlayers];
        for (int i = 0; i < this.maxPlayers; i++) {
            beis[i] = 1;
            mingBeis[i] = 1;
        }
        for (int i = 0; i < max; i++) {
            OptRecord record = records.get(i);
            if (record.getOptType() == OptType.mingpai) {
                long startFapaiTime = getFapaiStartTime();
                long currMillsTime = System.currentTimeMillis();
                long offset = currMillsTime - startFapaiTime;
                if (offset < 0) {
                    mingBeis[record.getSeatIndex()] = 5;
                } else if (startFapaiTime > 0) {
                    if (offset >= 4) {
                        mingBeis[record.getSeatIndex()] = 4;
                    } else if (offset >= 3) {
                        mingBeis[record.getSeatIndex()] = 3;
                    } else if (offset >= 2) {
                        mingBeis[record.getSeatIndex()] = 2;
                    } else {
                        mingBeis[record.getSeatIndex()] = 1;
                    }
                }
            } else if (record.getOptType() == OptType.qiangdizhu) {
                multiple *= 2;
            } else if (record.getOptType() == OptType.chu) {
                ChuRecord dr = (ChuRecord) record;
                if (dr.getType() == CardType.zha) {
                    multiple *= 2;
                } else if (dr.getType() == CardType.lianzha) {
                    int[] cards = dr.getDiscardCards();
                    int count = cards.length / 4;
                    if (count == 2) multiple *= 6;
                    else if (count == 3) multiple *= 12;
                    else if (count == 4) multiple *= 24;
                    else if (count == 5) multiple *= 48;
                }
            } else if (record.getOptType() == OptType.jiabei1) {
                beis[record.getSeatIndex()] *= 2;
            } else if (record.getOptType() == OptType.jiabei2) {
                beis[record.getSeatIndex()] *= 4;
            } else if (record.getOptType() == OptType.jiabei3) {
                beis[record.getSeatIndex()] *= 6;
            }
        }
        //地主加倍是给对方加，农民加倍是给自己加，最终农民的倍数之和是地主的倍数
        int dizhuIdx = this.getDizhuSeatIndex();
        int[] realBeis = new int[beis.length];
        for (int i = 0; i < realBeis.length; i++) {
            realBeis[i] = multiple * mingBeis[i];
        }
        if (dizhuIdx >= 0) {
            //先计算农民各自的加倍
            for (int j = 0; j < beis.length; j++) {
                if (j != dizhuIdx && beis[j] > 0) {
                    realBeis[j] *= beis[j];
                }
            }
            if (beis[dizhuIdx] > 0) {
                //计算地主给农民的加倍
                for (int j = 0; j < beis.length; j++) {
                    if (j != dizhuIdx) {
                        realBeis[j] *= beis[dizhuIdx];
                        if (mingBeis[dizhuIdx] > 1) {
                            realBeis[j] *= mingBeis[dizhuIdx];
                        }
                    }
                }
            }
            DiPaiType diPaiType = this.diPaiType(this.getDiCards());
            if (diPaiType != DiPaiType.NONE) {
                //计算底牌给农民的加倍
                for (int j = 0; j < beis.length; j++) {
                    if (j != dizhuIdx) {
                        realBeis[j] *= diPaiType.getBei();
                    }
                }
            }
            //把农民的倍数相加即得地主的加倍
            realBeis[dizhuIdx] = 0;
            for (int j = 0; j < realBeis.length; j++) {
                if (j != dizhuIdx) {
                    realBeis[dizhuIdx] += realBeis[j];
                }
            }
        }
        return realBeis;
    }

    /**
     * 是否已进行明牌操作
     *
     * @param seatIndex 玩家座位索引
     * @return true表示已明牌
     */
    public boolean isAlreadyMingPai(int seatIndex) {
        int max = records.size();
        for (int i = 0; i < max; i++) {
            OptRecord record = records.get(i);
            if (record.getOptType() == OptType.mingpai) {
                if (record.getSeatIndex() == seatIndex) return true;
            } else if (record.getOptType() == OptType.chu) {
                break;
            }
        }
        return false;
    }

    /**
     * 是否已进行加倍操作
     *
     * @param seatIndex 操作者座位索引
     * @return true 已进行加倍操作
     */
    public boolean isAlreadyJiabei(int seatIndex) {
        int max = records.size();
        for (int i = 0; i < max; i++) {
            OptRecord record = records.get(i);
            if (record.getOptType() == OptType.jiabei1
                    || record.getOptType() == OptType.jiabei2
                    || record.getOptType() == OptType.jiabei3
                    || record.getOptType() == OptType.buJiabei) {
                if (record.getSeatIndex() == seatIndex) return true;
            } else if (record.getOptType() == OptType.chu) {
                break;
            }
        }
        return false;
    }

    /**
     * 全部玩家是否已进行加倍操作
     *
     * @return true 全部已完成加倍操作
     */
    public boolean allAlreadyJiabei() {
        int max = records.size();
        int count = 0;
        for (int i = 0; i < max; i++) {
            OptRecord record = records.get(i);
            if (record.getOptType() == OptType.jiabei1
                    || record.getOptType() == OptType.jiabei2
                    || record.getOptType() == OptType.jiabei3
                    || record.getOptType() == OptType.buJiabei) {
                count++;
            } else if (record.getOptType() == OptType.chu) {
                break;
            }
        }
        return count == maxPlayers;
    }

    /**
     * 地主是否打出春天而赢牌 </br>
     * 必须是在出玩牌后再调用
     *
     * @return true表示地主打出春天牌
     */
    public boolean isSpringByDizhu() {
        for (OptRecord record : records) {
            if (record.getOptType() == OptType.chu) {
                if (record.getSeatIndex() != this.getDizhuSeatIndex()) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 农民是否打出春天而赢牌 </br>
     * 必须是在出玩牌后再调用
     *
     * @return true表示农民打出春天牌
     */
    public boolean isSpringByFarmer() {
        int dizhuChuCount = 0;
        for (OptRecord record : records) {
            if (record.getOptType() == OptType.chu) {
                if (record.getSeatIndex() == this.getDizhuSeatIndex()) {
                    dizhuChuCount++;
                }
            }
        }
        return dizhuChuCount == 1;
    }

    public int[] jiaBei() {
        int[] jiaBes = new int[maxPlayers];
        for (OptRecord record : records) {
            if (record.getOptType() == OptType.jiabei1
                    || record.getOptType() == OptType.jiabei2
                    || record.getOptType() == OptType.jiabei3) {
                jiaBes[record.getSeatIndex()] = record.getOptType().getId();
            } else if (record.getOptType() == OptType.chu) break;
        }
        return jiaBes;
    }

    /**
     * 是否不叫地主
     *
     * @param seatIndex
     * @return
     */
    public boolean isPassDiZhu(int seatIndex) {
        for (OptRecord record : records) {
            if (record.getOptType() == OptType.chu) break;
            if (record.getSeatIndex() == seatIndex && (record.getOptType() == OptType.bujiaodizhu || record.getOptType() == OptType.buqiangdizhu)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否明牌
     *
     * @param seatIndex
     * @return
     */
    public boolean isMingPai(int seatIndex) {
        for (OptRecord record : records) {
            if (record.getOptType() == OptType.chu) break;
            if (record.getOptType() == OptType.mingpai && record.getSeatIndex() == seatIndex) return true;
        }
        return false;
    }

    public List<OptRecord> getRecords() {
        return this.records;
    }

    /**
     * 为记牌器赋值
     *
     * @param counter 玩家对应自身的记牌器
     * @param card    记牌器中需减少的牌
     */
    private void calcCounter(int[] counter, int card) {
        int point = Cards.getPoint(card);
        if (point == 2) {
            counter[12]--;
        } else if (point == 0) {
            int suit = Cards.getSuit(card);
            if (suit == SuitType.xiaowang.getId()) {
                counter[13]--;
            } else {
                counter[14]--;
            }
        } else {
            counter[point - 3]--;
        }
    }

    /**
     * 记牌器
     *
     * @return 每个玩家对应的记牌器，为15个长度的数组
     */
    public int[][] getCardCounter() {
        int[][] counters = new int[this.maxPlayers][];
        for (int i = 0; i < counters.length; i++) {
            counters[i] = new int[]{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1};
        }
        for (int i = 0; i < counters.length; i++) {
            Player player = this.players[i];
            Cards handCards = player.getMycards();
            if (handCards != null && handCards.getCardCount() > 0) {
                int[] cards = handCards.getCards();
                for (int card : cards) {
                    calcCounter(counters[i], card);
                }
            }
        }
        int size = this.records.size();
        int last = size - 1;
        for (int i = last; i >= 0; i--) {
            OptRecord record = this.records.get(i);
            if (record.getOptType() != OptType.chu && record.getOptType() != OptType.pass) break;
            if (record.getOptType() == OptType.chu) {
                ChuRecord chuRecord = (ChuRecord) record;
                int[] chuCards = chuRecord.getDiscardCards();
                for (int card : chuCards) {
                    for (int j = 0; j < counters.length; j++) {
                        calcCounter(counters[j], card);
                    }
                }
            }
        }
        return counters;
    }

    @Override
    public void reset() {
        for (Player player : players) {
            if (player != null) {
                player.reset();
            }
        }
        this.records.clear();
    }

    public TableState getState() {
        return state;
    }

    public void setState(TableState state) {
        this.state = state;
    }

    public TableInfo toDeskInfo() {
        TableInfo info = new TableInfo();
        info.setMode(this.mode);
        info.setPlace(this.place);
        info.setPlayers(JsonUtils.toJsonString(this.players));
        info.setRecords(JsonUtils.toJsonString(this.records));
        info.setBankerSeatIndex(this.bankerSeatIndex);
        info.setId(this.id);
        info.setMaxPlayers(this.maxPlayers);
        info.setCreateTimestamp(this.createTimestamp);
        info.setStep(this.step);
        info.setState(this.state.getValue());
        info.setFapaiStartTime(this.fapaiStartTime);
        info.setLaiziPoint(this.laiziPoint);
        return info;
    }
}
